【SFA官方翻译】:Spring中的组件扫描
原文链接:https://www.baeldung.com/spring-component-scanning
作者:baeldung
译者:ForeverL888
1. 概览
在本文中,我们来回顾一下Spring中组件的扫描。当我们在工作中使用Spring,我们会在类上使用注解来让这些类成为Spring的 beans。但是,除此之外,我们还可以告诉Spring到哪里去获取这些被注解的类。因为不是所有的使用注解的类,都需要在本次运行中成为Spring中的beans。
当然,Spring提供了默认的组件扫描策略,但是我们同样可以自定义哪些包可以进行扫描。
首先,我们来看一看默认配置。
2. 无参的@ComponentScan
2.1. 在Spring应用中使用@ComponentScan
在Spring中,我们使用@ComponentScan注解和@Configuration注解来指定我们所想要进行扫描的包。无参的@ComponentScan告诉Spring扫描当前包及其子包下所有的类。
我们在com.baeldung.componentscan.springapp包有一个使用@Configuration注解的类:
@Configuration
@ComponentScan
public class SpringComponentScanApp {
private static ApplicationContext applicationContext;
@Bean
public ExampleBean exampleBean() {
return new ExampleBean();
}
public static void main(String[] args) {
applicationContext = new AnnotationConfigApplicationContext(SpringComponentScanApp.class);
for (String beanName : applicationContext.getBeanDefinitionNames()) {
System.out.println(beanName);
}
}
}
另外,我们在 com.baeldung.componentscan.springapp.animals包下有Cat和Dog两个组件:
package com.baeldung.componentscan.springapp.animals;
// ...
@Component
public class Cat {}
package com.baeldung.componentscan.springapp.animals;
// ...
@Component
public class Dog {}
最后,我们在com.baeldung.componentscan.springapp.flowers 包下有Rose组件:
package com.baeldung.componentscan.springapp.flowers;
// ...
@Component
public class Rose {}
在main() 方法的输出中会包含com.baeldung.componentscan.springapp 包及其子包下所有的beans:
springComponentScanApp
cat
dog
rose
exampleBean
注意主应用程序类也是一个bean,因为它使用了@Configuration注解,该注解的实质是@Component。
同时也要注意的是主应用程序类和配置类不需要一定要放在一起。如果它们在不同的类中,那么把主应用程序类放在哪里是无关紧要的。只有配置类的位置才是至关重要的,因为组件扫描默认是从配置类所在的包及其子包开始的。
最后,注意下,我们示例中的@ComponentSacn等价于
@ComponentScan(basePackages = "com.baeldung.componentscan.springapp")
其中basePackages 参数是指定用于扫描的包或者是包的数组。
2.2. 在Spring应用中使用@SpringBootApplication
Spring Boot的很多技巧都是隐含的。我们使用@SpringBootApplication 注解,实际上该注解由其它的三个注解组成:
@Configuration
@EnableAutoConfiguration
@ComponentScan
让我们在com.baeldung.componentscan.springbootapp 包下创建一个类型的结构。这时候,我们的主应用程序类是这样子的:
package com.baeldung.componentscan.springbootapp;
// ...
@SpringBootApplication
public class SpringBootComponentScanApp {
private static ApplicationContext applicationContext;
@Bean
public ExampleBean exampleBean() {
return new ExampleBean();
}
public static void main(String[] args) {
applicationContext = SpringApplication.run(SpringBootComponentScanApp.class, args);
checkBeansPresence("cat", "dog", "rose", "exampleBean", "springBootComponentScanApp");
}
private static void checkBeansPresence(String... beans) {
for (String beanName : beans) {
System.out.println("Is " + beanName + " in ApplicationContext: " +
applicationContext.containsBean(beanName));
}
}
}
其它的包和类都保持不变,我们会将它们拷贝到com.baeldung.componentscan.springbootapp 包下。
Spring Boot扫描的包和前面例子非常类似,让我们来看下该例子的输出结果:
Is cat in ApplicationContext: true
Is dog in ApplicationContext: true
Is rose in ApplicationContext: true
Is exampleBean in ApplicationContext: true
Is springBootComponentScanApp in ApplicationContext: true
在第二个示例中,我们不会打印出所有的beans,而是判断这些beans是否存在。这种做法的原因是:如果打印出所有的beans,那么输出结果将会变的非常冗余。
这是因为隐含的@EnableAutoConfiguration 注解会根据pom.xml文件中的依赖自动创建非常多的beans。
3. 使用带参的 @ComponentScan
现在,让我们自定义下可扫描的路径。比如说,我们想要排除扫描Rose这个bean。
我们有多种方式可以实现这一目的。首先我们可以通过改变basePackages参数:
@ComponentScan(basePackages = "com.baeldung.componentscan.springapp.animals")
@Configuration
public class SpringComponentScanApp {
// ...
}
现在,我么可以看到输出结果是
springComponentScanApp
cat
dog
exampleBean
让我们看看这背后是什么:
springComponentScanApp 作为一个配置bean被创建,同时它也是作为传递给AnnotationConfigApplicationContext的一个参数
exampleBean 是在配置类中的一个内部配置bean
cat 和dog 两个bean是被指定的 com.baeldung.componentscan.springapp.animals 包名下的bean
另一种方式是使用一个过滤器,通过指定的正则表达式来过滤掉某些类:
@ComponentScan(excludeFilters =
@ComponentScan.Filter(type=FilterType.REGEX,
pattern="com\\.baeldung\\.componentscan\\.springapp\\.flowers\\..*"))
当然,我们也可以使用另外一种过滤类型:
@ComponentScan(excludeFilters =
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = Rose.class))
上面所列的自定义的方式同样也是适合于Spring Boot的。我们可以同时使用@ComponentScan 和 @SpringBootApplication注解,并且它产生的效果是一样的:
@SpringBootApplication
@ComponentScan(basePackages = "com.baeldung.componentscan.springbootapp.animals")
4. 默认包
我们应当尽量避免将@Configuration注解的类,放置到默认包下(对一个类没有使用任何的包名进行修饰)。在这种情况下,Spring会扫描类路径下的所有的jar中的类文件。这是会产生错误并且导致我们的应用程序可能无法正常启动。
5. 结论
在这个文章中,我们学到了spring默认扫描哪些的包路径,也学到了如何去自定义这些包路径。 和往常一样,完整的示例代码在github上。